extends CharacterBody3D
class_name Player
# 这里是有自带模板的，不过还是仿照教程 BVNy4y1q71z 改一下
# 另外视频教程中键位还没有Physics的键盘代码，大概是新的功能吧
# GD4.0版本这里velocity已经是CharacterBody3D自带的一个属性了23333

#注意在地图中不要随意旋转player！h v的值是要拿来关联使用的


const SNEAK_SPEED_FACTOR = 0.2
const JUMP_VELOCITY = 4.5
signal rotate_cube(location:Vector3, direction:Vector3i)
#@export var can_jump:bool = true


@onready var check_point := transform
@onready var camera_root := $CameraRoot
@onready var spring_arm_3d:SpringArm3D = %SpringArm3D


# 局部坐标，操作了之后乘上global_basis即可（官方velocity是绝对坐标
# 好吧有了setget以后直接变成中介转换器了
var local_velocity:Vector3 = Vector3(0,0,0):	 
	set(value):
		velocity = global_basis * value
	get:
		return global_basis.inverse() * velocity

# Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
var gravity:float = ProjectSettings.get_setting("physics/3d/default_gravity")
#var gravity = 0
@export_enum("Normal:0","No gravity:1","Revertable:2") var gravity_mode:int = 0 # 如果设定无重力，到天花板上跳会反向跳
#var gravity_freeze:bool = false
@export var gravity_ratio:float = 1
@export var move_speed = 5.0
@export var jump_height:float = 1
@export var travel_limit:int = 200

var camera_rotation_state:Vector2: # 不会实时更新，只在需要时检查
	set(value):
		camera_root.camrot_h = value.x
		camera_root.camrot_v = value.y
	get:
		return Vector2(camera_root.camrot_h,camera_root.camrot_v)

var camera_distance:float:
	set(value):
		#spring_arm_3d.spring_length = value.z
		spring_arm_3d.distance = value
	get:
		return spring_arm_3d.distance

func is_on_local_ground(direction:Vector3 = Vector3.UP) -> bool:
	# 神奇的改变重力大法，官方都没有提供这个方法……只有自己写了
	#var marker:bool = false
	# 碰撞底面法线符合要求则视作在“局部地面”上
	for collision_id in range(0,get_slide_collision_count()) :
		if (global_basis * direction).dot(get_slide_collision(collision_id).get_normal() ) > cos (PI/4) :
			return true # 有一个是朝上的就可以
	return false

func _process(delta):
	#落下归位
	if global_position.length() > travel_limit:
		transform = check_point
		velocity = Vector3.ZERO
	
	match gravity_mode: # 不放这里的话触摸按键反应不了
		0:
			#if not is_on_local_ground():
				#local_velocity.y -= gravity * delta * gravity_ratio
			# Handle Jump.
			if Input.is_action_just_pressed(&"jump") and is_on_local_ground():
				local_velocity.y = JUMP_VELOCITY * sqrt(jump_height)
			#宁可允许爬墙，也不能有奇怪的浮空表现
			#但是这样又会导致“加速下落”
			
		1: 
			pass #跳都不让跳
		2:
			#这个不能触底就停，因为有可能要升起，此时还在地上
			#local_velocity.y -= gravity * delta * gravity_ratio
			if Input.is_action_just_pressed(&"jump") and (is_on_local_ground() or is_on_local_ground(Vector3.DOWN)):
				gravity_ratio *= -1
	
	
	
func _physics_process(delta):
	
	# 重力和跳跃分开处理
	#好吧，“残影”问题现在恐怕解不了，毕竟是动画必经之路
	match gravity_mode: # 不放这里的话触摸按键反应不了
		0:
			if not is_on_local_ground():
				local_velocity.y -= gravity * delta * gravity_ratio
		1: 
			pass 
		2:
			#这个不能触底就停，因为有可能要升起，此时还在地上
			local_velocity.y -= gravity * delta * gravity_ratio
	

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	#后面观察了一下原模板代码，发现只要把方向加上去就能用了（get_vector方式使得获取输入的代码十分简洁了，这个可能是新加的函数？）
	var h_rot = $CameraRoot/h.basis.get_euler().y
	#var input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
	#var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).rotated(Vector3.UP, h_rot).normalized()
#
	#if direction:
		#velocity.x = direction.x * move_speed
		#velocity.z = direction.z * move_speed
		#if Input.is_action_pressed("sneak"):# 然而并没有防坠落的功能
			#velocity.x *= SNEAK_SPEED_FACTOR
			#velocity.z *= SNEAK_SPEED_FACTOR
	#else: #减速器
		#velocity.x = move_toward(velocity.x, 0, move_speed)
		#velocity.z = move_toward(velocity.z, 0, move_speed)
#
	#move_and_slide()
	
	#var move_direction := Vector3.ZERO  #归零器
	var input_dir = Input.get_vector(&"move_left", &"move_right", &"move_forward", &"move_backward")
	var move_direction = (Vector3(input_dir.x, 0, input_dir.y)).rotated(Vector3.UP, h_rot)
	move_direction = move_direction.normalized() * clamp(move_direction.length(),0,1)
	local_velocity.x = move_direction.x * move_speed 
	local_velocity.z = move_direction.z * move_speed
	if Input.is_action_pressed(&"sneak"):# 然而并没有防坠落的功能
			local_velocity.x *= SNEAK_SPEED_FACTOR
			local_velocity.z *= SNEAK_SPEED_FACTOR
	# velocity本身是绝对坐标
	
	move_and_slide()


func _on_camera_root_rotate_cube(direction):
	rotate_cube.emit(position,direction)
